package com.gking.centerSystem.realm;

import com.alibaba.fastjson.JSON;

import com.gking.centerSystem.entity.dto.RolePermissionListDto;
import com.gking.centerSystem.entity.dto.UserRoleListDto;
import com.gking.centerSystem.entity.User;
import com.gking.centerSystem.service.RolePermissions.RolePermissionsService;
import com.gking.centerSystem.service.User.UserService;
import com.gking.centerSystem.service.UserRoles.UserRolesService;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.TimeUnit;

import static com.gking.centerSystem.common.RedisConstants.*;

@Slf4j
@Component
public class MyRealm extends AuthorizingRealm {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private UserService userService;

    @Resource
    private UserRolesService userRolesService;

    @Resource
    private RolePermissionsService rolePermissionsService;

    //自定义授权方法：获取当前登录用户权限信息，返回给 Shiro 用来进行授权对比
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //1、获取用户身份信息
        String principal = principalCollection.getPrimaryPrincipal().toString();
        log.info("MyRealm 自定义授权方法，查询到的用户 principal : {}", principal);

        List<String> roleList;
        List<String> permissionsList;
        String rolesString = stringRedisTemplate.opsForValue().get(Login_User_Roles + principal);
        String permissionsString = stringRedisTemplate.opsForValue().get(Login_User_Permissions + principal);

        try {
            if (rolesString == null || permissionsString == null) {
                //2、调用业务层获取用户的角色信息(数据库)
                UserRoleListDto userRoleListDto = userRolesService.getUserRolesDetail(principal.substring(0, principal.length() - 4)).getData();
                List<String> roleIdList = userRoleListDto.getRoleIdList();
                if (roleIdList == null || roleIdList.isEmpty()) return null;

                roleList = userRoleListDto.getRoleNameList();
                stringRedisTemplate.opsForValue().set(Login_User_Roles + principal,
                        JSON.toJSONString(roleList), Login_User_Info_TTL, TimeUnit.HOURS);

                //2.1 调用业务层获取用户的权限信息（数据库）
                permissionsList = userRoleListDto.getPermissionList();
                stringRedisTemplate.opsForValue().set(Login_User_Permissions + principal,
                        JSON.toJSONString(permissionsList), Login_User_Info_TTL, TimeUnit.HOURS);
            } else {
                // 如何redis中有角色权限缓存，则使用redis中的角色权限
                roleList = JSON.parseArray(rolesString, String.class);
                permissionsList = JSON.parseArray(permissionsString, String.class);
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
//        log.warn("当前用户角色信息 = " + roleList);
//        log.warn("当前用户权限信息 = " + permissionsList);

        //3、 创建对象，存储当前登录的用户的权限和角色
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.addRoles(roleList);
        info.addStringPermissions(permissionsList);

        //4、 返回信息
        return info;
    }

    @Override
    public boolean supports(AuthenticationToken token) {
        // Confirm that this realm supports CustomToken
        return token instanceof UsernamePasswordToken;
    }

    //自定义登录认证方法
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //1、获取用户身份信息
        String principal = authenticationToken.getPrincipal().toString();
        User user = userService.getById(principal.substring(0, principal.length() - 4));
        log.warn("shiro 获取用户身份信息 userId : {}", principal);
        log.warn("shiro 用户信息 user : {}", user);

        //3、非空判断，将数据封装返回
        if (user == null) return null;

        Md5Hash md5Hash = new Md5Hash(user.getPassword(), "salt", 3);
        AuthenticationInfo info = new SimpleAuthenticationInfo(
                authenticationToken.getPrincipal().toString(),//用户标识
                md5Hash.toHex(),//经过加密处理后的用户凭据（密码）
                ByteSource.Util.bytes("salt"),//加密时的盐 ByteSource.Util.bytes("salt")
                authenticationToken.getPrincipal().toString()//realm name
        );

        return info;
    }


    //清除用户缓存
    public void clearCached() {
        PrincipalCollection principals = SecurityUtils.getSubject().getPrincipals();
        log.warn("清除缓存 principals： {}", principals);
        super.clearCache(principals);
    }

}
